#ifndef __SHM__HPP__
#define __SHM__HPP__

#include <iostream>
#include <cstdio>
#include <string>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cerrno>

#define gCreater 1
#define gUser 2
const int gProc_id = 0x66;
const int gShmSize = 4096;
const std::string pathname = "/home/HCC/linux/code11";

class Shm
{
private:
    std::string ToHex(key_t key)
    {
        char buffer[gShmSize];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }
    key_t GetCommKey()
    {
        int n = ftok(_pathname.c_str(), _proc_id);
        if (n < 0)
        {
            perror("ftol");
        }
        return n;
    }
    int GetShmHelper(key_t key, int size, int mode)
    {
        return shmget(key, size, mode);
    }
    void GetShmUserCreater()
    {
        if (_who == gCreater)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid == -1)
            {
                perror("shmget");
            }
            std::cout << "creater done ..." << std::endl;
        }
    }
    void GetShmForUser()
    {
        if (_who == gUser)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
            if (_shmid == -1)
            {
                perror("shget");
            }
            std::cout << "shm get done ..." << std::endl;
        }
    }
    std::string To_String(int _who)
    {
        if (_who == gUser)
            return "User";
        else if (_who == gCreater)
            return "Creater";
        else
            return "None";
    }
    void DatachShm(void * addrshm)
    {
        if(addrshm==nullptr)
        {
            return;
        }
        shmdt(addrshm);
        std::cout<<To_String(_who)<<" detachshm done"<<std::endl;
    }
    void *AttachShm()
    {
        if (_addrshm == nullptr)
            DatachShm(_addrshm);
        void *shmaddr = shmat(_shmid, nullptr, 0);
        if (shmaddr == nullptr)
        {
            perror("shmat");
        }
        return shmaddr;
        std::cout << To_String(_who) << " shmat done " << std::endl;
    }

public:
    Shm(const std::string &pathname, int who, int proc_id)
        : _pathname(pathname), _proc_id(proc_id), _who(who)
    {
        _key = GetCommKey();
        if (_who == gCreater)
        {
            GetShmUserCreater();
        }
        else if (_who == gUser)
        {
            GetShmForUser();
        }
        _addrshm = AttachShm();
        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "key: " << ToHex(_key) << std::endl;
    }
    ~Shm()
    {
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
            if (res == -1)
            {
                perror("shmctl");
            }
        }
        std::cout << "shm remove done ... " << std::endl;
    }
    void *AddrShm()
    {
        return _addrshm;
    }

    void Zero()
    {
        if(_addrshm)
        {
            memset(_addrshm,0,gShmSize);
        }
    }
private:
    key_t _key;
    int _shmid;

    std::string _pathname;
    int _proc_id;

    int _who;
    void *_addrshm;
};
#endif 